#include "stdafx.h"

#include "hwindatetime.h"
#include "hwinenvironment.h"
#include "hwinexception.h"
#include "hwincalendar.h"
#include "hwinresources.h"



namespace harlinn
{
    namespace windows
    {
        namespace
        {
            const long long TicksPerMillisecond = 10000;
            const long long TicksPerSecond = TicksPerMillisecond * 1000; 
            const long long TicksPerMinute = TicksPerSecond * 60;
            const long long TicksPerHour = TicksPerMinute * 60; 
            const long long TicksPerDay = TicksPerHour * 24; 

            const double MillisecondsPerTick = 1.0 / TicksPerMillisecond;
            const double SecondsPerTick =  1.0 / TicksPerSecond;
            const double MinutesPerTick = 1.0 / TicksPerMinute;
            const double HoursPerTick = 1.0 / TicksPerHour;
            const double DaysPerTick = 1.0 / TicksPerDay;

            const unsigned long long KindUnspecified        = 0x0000000000000000;
            const unsigned long long KindUtc                = 0x4000000000000000; 
            const unsigned long long KindLocal              = 0x8000000000000000;
            const unsigned long long TicksMask              = 0x3FFFFFFFFFFFFFFF; 
            const unsigned long long FlagsMask              = KindUtc | KindLocal;
            
            const int KindShift = 62;
            
            const int MillisPerSecond = 1000;
            const int MillisPerMinute = MillisPerSecond * 60;
            const int MillisPerHour = MillisPerMinute * 60;
            const int MillisPerDay = MillisPerHour * 24; 

            
            const int DaysPerYear = 365; 
            const int DaysPer4Years = DaysPerYear * 4 + 1; 
            const int DaysPer100Years = DaysPer4Years * 25 - 1;
            const int DaysPer400Years = DaysPer100Years * 4 + 1; 

            
            const int DaysTo1601 = DaysPer400Years * 4; 
            const int DaysTo1899 = DaysPer400Years * 4 + DaysPer100Years * 3 - 367; 
            const int DaysTo10000 = DaysPer400Years * 25 - 366;

            const long long MinTicks = 0; 
            const long long MaxTicks = DaysTo10000 * TicksPerDay - 1;
            const long long MaxMillis = (long long)DaysTo10000 * MillisPerDay; 
 
            const long long FileTimeOffset = DaysTo1601 * TicksPerDay;
            const long long DoubleDateOffset = DaysTo1899 * TicksPerDay;

            
            const long long OADateMinAsTicks = (DaysPer100Years - DaysPerYear) * TicksPerDay;

            const int DaysToMonth365[13] = 
            {
                0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
            }; 
            const int DaysToMonth366[13] = 
            { 
                0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366
            };

            const double OADateMinAsDouble = -657435.0;
            const double OADateMaxAsDouble = 2958466.0; 

        }


        // ----------------------------------------------------------------------
        // TimeSpan
        // ----------------------------------------------------------------------
        HWIN_EXPORT const TimeSpan TimeSpan::Zero(0);

        
 
        HWIN_EXPORT const TimeSpan TimeSpan::MaxValue(MAXINT64); 
        HWIN_EXPORT const TimeSpan TimeSpan::MinValue(MININT64);

        HWIN_EXPORT long long TimeSpan::TimeToTicks(int theDays, int theHours, int theMinutes, int theSeconds, int theMilliseconds)
        {
            long long milliSeconds = ((long long)theDays * 3600 * 24 + 
                                            (long long)theHours * 3600 + 
                                            (long long)theMinutes * 60 + theSeconds) * 1000 + 
                                            theMilliseconds; 

            if (milliSeconds > MaxMilliSeconds || milliSeconds < MinMilliSeconds)
            {
                throw ArgumentOutOfRangeException("", Environment::GetResourceString(Resources::OVERFLOW_TIMESPANTOOLONG));
            }
            long long result =  milliSeconds * TicksPerMillisecond;
            return result;
        }

        HWIN_EXPORT TimeSpan TimeSpan::Add(const TimeSpan& theOther) const 
        {
            long long result = ticks + theOther.ticks; 
            if ((ticks >> 63 == theOther.ticks >> 63) && (ticks >> 63 != result >> 63)) 
            {
                throw OverflowException(Environment::GetResourceString(Resources::OVERFLOW_TIMESPANTOOLONG));
            }
            return TimeSpan(result); 
        } 

        HWIN_EXPORT TimeSpan TimeSpan::Interval(double theValue, int theScale) 
        {
            if (_isnan(theValue))
            {
                throw ArgumentException(Environment::GetResourceString(Resources::ARG_CANNOTBENAN)); 
            }
            double milliSeconds = theValue * theScale; 
            milliSeconds = milliSeconds + (theValue >= 0? 0.5: -0.5); 

            if ((milliSeconds > MAXINT64 / TicksPerMillisecond) || (milliSeconds < MININT64 / TicksPerMillisecond))
            {
                throw OverflowException(Environment::GetResourceString(Resources::OVERFLOW_NEGATETWOSCOMPNUM)); 
            }
            return TimeSpan((long long)milliSeconds * TicksPerMillisecond);
        }


        HWIN_EXPORT TimeSpan TimeSpan::Duration() const
        { 
            if (ticks == MinValue.ticks)
            {
                throw OverflowException(Environment::GetResourceString(Resources::OVERFLOW_DURATION)); 
            }
            return TimeSpan(ticks >= 0? ticks: -ticks);
        }

        HWIN_EXPORT TimeSpan TimeSpan::Negate() const
        {
            if (ticks==MinValue.ticks) 
            {
                throw OverflowException(Environment::GetResourceString(Resources::OVERFLOW_NEGATETWOSCOMPNUM));
            }
            return TimeSpan(-ticks); 
        }

        HWIN_EXPORT TimeSpan TimeSpan::Subtract(const TimeSpan& theOther) const 
        {
            long long result = ticks - theOther.ticks; 
            if ((ticks >> 63 != theOther.ticks >> 63) && (ticks >> 63 != result >> 63))
            {
                throw OverflowException(Environment::GetResourceString(Resources::OVERFLOW_TIMESPANTOOLONG));
            }
            return TimeSpan(result);
        } 

        HWIN_EXPORT String TimeSpan::ToString() const
        {
            wchar_t buffer[128];
            size_t length = GetDurationFormat(LOCALE_USER_DEFAULT,0,nullptr,ticks,nullptr, buffer, 128);
            if(length == 0)
            {
                ThrowLastOSError();    
            }
            return String(buffer,length);
        }

        HWIN_EXPORT double TimeSpan::TotalDays() const
        { 
            return ((double)ticks) * DaysPerTick;
        }

        HWIN_EXPORT double TimeSpan::TotalHours() const
        { 
            return (double(ticks)) * HoursPerTick; 
        } 

        HWIN_EXPORT double TimeSpan::TotalMilliseconds() const
        {
            double result = ((double)ticks) * MillisecondsPerTick; 
            if (result > MaxMilliSeconds)
            {
                return (double)MaxMilliSeconds; 
            }
 
            if (result < MinMilliSeconds)
            {
                return (double)MinMilliSeconds; 
            }

            return result;
        }

        HWIN_EXPORT double TimeSpan::TotalMinutes() const
        { 
            return (double)ticks * MinutesPerTick;
        }

        HWIN_EXPORT double TimeSpan::TotalSeconds() const
        { 
            return (double)ticks * SecondsPerTick; 
        } 

        // ----------------------------------------------------------------------
        // DateTime
        // ----------------------------------------------------------------------
        
            

        HWIN_EXPORT const DateTime DateTime::MinValue(MinTicks, DateTimeKind::Unspecified);
        HWIN_EXPORT const DateTime DateTime::MaxValue(MaxTicks, DateTimeKind::Unspecified);

        


        HWIN_EXPORT DateTime::DateTime(long long ticks) 
        { 
            if (ticks < MinTicks || ticks > MaxTicks)
            {
                throw ArgumentOutOfRangeException("ticks", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_DATETIMEBADTICKS)); 
            }
            data = (unsigned long long)ticks;
        } 

        HWIN_EXPORT DateTime::DateTime(unsigned long long theDateData) 
            : data(theDateData)
        {
            
        } 


        HWIN_EXPORT DateTime::DateTime(long long ticks, DateTimeKind kind) 
        { 
            if (ticks < MinTicks || ticks > MaxTicks) 
            { 
                throw ArgumentOutOfRangeException("ticks", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_DATETIMEBADTICKS));
            } 
            if (kind < DateTimeKind::Unspecified || kind > DateTimeKind::Local) 
            {
                throw ArgumentException(Environment::GetResourceString(Resources::ARGUMENT_INVALIDDATETIMEKIND), "kind");
            }
            
            data = ((unsigned long long)ticks | ((unsigned long long)kind << KindShift));
        } 


        HWIN_EXPORT long long DateTime::ToTicks(int year, int month, int day, const globalization::Calendar& calendar) 
        {
            return ToTicks(year, month, day,0,0,0,0,calendar);
        }

        HWIN_EXPORT long long DateTime::ToTicks(int year, int month, int day, int hour, int minute, int second, int millisecond,const globalization::Calendar& calendar) 
        {
            if (millisecond < 0 || millisecond >= MillisPerSecond) 
            { 
                throw ArgumentOutOfRangeException("millisecond", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_RANGE, 0, MillisPerSecond - 1));
            } 
            long long ticks = calendar.ToDateTime(year, month, day, hour, minute, second, 0).data & TicksMask;
            ticks += millisecond * TicksPerMillisecond; 
            if (ticks < MinTicks || ticks > MaxTicks)
            {
                throw ArgumentException(Environment::GetResourceString(Resources::ARG_DATETIMERANGE));
            }
            return ticks;
        }

        HWIN_EXPORT long long DateTime::DateToTicks(int year, int month, int day) 
        { 
            if (year >= 1 && year <= 9999 && month >= 1 && month <= 12) 
            { 
                const int* days = IsLeapYear(year)? DaysToMonth366: DaysToMonth365;
                if (day >= 1 && day <= days[month] - days[month - 1]) 
                { 
                    int y = year - 1;
                    int n = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1;
                    return n * TicksPerDay;
                } 
            }
            throw ArgumentOutOfRangeException(nullptr, Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_BADYEARMONTHDAY)); 
        } 

        HWIN_EXPORT long long DateTime::SystemTimeToTicks(const SYSTEMTIME& systemTime)
        {
            if (systemTime.wMilliseconds >= MillisPerSecond) 
            {
                throw ArgumentOutOfRangeException("systemTime.wMilliseconds", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_RANGE, 0, MillisPerSecond - 1)); 
            }
            unsigned long long ticks = DateToTicks(systemTime.wYear, systemTime.wMonth, systemTime.wDay) + TimeToTicks(systemTime.wHour, systemTime.wMinute, systemTime.wSecond);
            ticks += systemTime.wMilliseconds * TicksPerMillisecond; 
            if (ticks < MinTicks || ticks > MaxTicks)
            {
                throw new ArgumentException(Environment::GetResourceString(Resources::ARG_DATETIMERANGE)); 
            }
            return ticks;
        }

        HWIN_EXPORT long long DateTime::TimeToTicks(int hour, int minute, int second)
        {
            if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >=0 && second < 60) 
            { 
                return TimeSpan::TimeToTicks(hour, minute, second);
            } 
            throw ArgumentOutOfRangeException(nullptr, Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_BADHOURMINUTESECOND));
        }


        HWIN_EXPORT DateTime::DateTime(int year, int month, int day) 
        {
            data = (unsigned long long) DateToTicks(year, month, day);
        }

        HWIN_EXPORT DateTime::DateTime(int year, int month, int day, const globalization::Calendar& calendar)
            : data(ToTicks( year, month, day, 0, 0, 0,0, calendar)) 
        {
        }


        HWIN_EXPORT DateTime::DateTime(int year, int month, int day, int hour, int minute, int second) 
            : data(DateToTicks(year, month, day) + TimeToTicks(hour, minute, second))
        {
        }

        HWIN_EXPORT DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) 
        {
            if (kind < DateTimeKind::Unspecified || kind > DateTimeKind::Local) 
            { 
                throw ArgumentException(Environment::GetResourceString(Resources::ARGUMENT_INVALIDDATETIMEKIND), "kind");
            } 
            long long ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second);
            data = ((long long)ticks | ((long long)kind << KindShift)); 
        }

        HWIN_EXPORT DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, const globalization::Calendar& calendar) 
        { 
            data = (unsigned long long)calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks();
        }

        HWIN_EXPORT DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond) 
        { 
            if (millisecond < 0 || millisecond >= MillisPerSecond) 
            {
                throw ArgumentOutOfRangeException("millisecond", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_RANGE, 0, MillisPerSecond - 1)); 
            }
            long long ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second);
            ticks += millisecond * TicksPerMillisecond; 
            if (ticks < MinTicks || ticks > MaxTicks)
            {
                throw new ArgumentException(Environment::GetResourceString(Resources::ARG_DATETIMERANGE)); 
            }
            data = (unsigned long long)ticks;
        }

        
 
        HWIN_EXPORT DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind) 
        {
            if (millisecond < 0 || millisecond >= MillisPerSecond) 
            {
                throw ArgumentOutOfRangeException("millisecond", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_RANGE, 0, MillisPerSecond - 1));
            } 
            if (kind < DateTimeKind::Unspecified || kind > DateTimeKind::Local) 
            {
                throw ArgumentException(Environment::GetResourceString(Resources::ARGUMENT_INVALIDDATETIMEKIND), "kind"); 
            } 
            long long ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); 
            ticks += millisecond * TicksPerMillisecond;
            if (ticks < MinTicks || ticks > MaxTicks)
            {
                throw ArgumentException(Environment::GetResourceString(Resources::ARG_DATETIMERANGE));
            }
            data = ((unsigned long long)ticks | ((unsigned long long)kind << KindShift)); 
        }
 
        HWIN_EXPORT DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, const globalization::Calendar& calendar) 
        {
            if (millisecond < 0 || millisecond >= MillisPerSecond) 
            { 
                throw ArgumentOutOfRangeException("millisecond", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_RANGE, 0, MillisPerSecond - 1));
            } 
            long long ticks = calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks();
            ticks += millisecond * TicksPerMillisecond; 
            if (ticks < MinTicks || ticks > MaxTicks)
            {
                throw ArgumentException(Environment::GetResourceString(Resources::ARG_DATETIMERANGE));
            }
            data = (unsigned long long)ticks;
        } 

        HWIN_EXPORT DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, const globalization::Calendar& calendar, DateTimeKind kind) 
        { 
            if (millisecond < 0 || millisecond >= MillisPerSecond) 
            { 
                throw ArgumentOutOfRangeException("millisecond", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_RANGE, 0, MillisPerSecond - 1));
            }
            if (kind < DateTimeKind::Unspecified || kind > DateTimeKind::Local) 
            {
                throw ArgumentException(Environment::GetResourceString(Resources::ARGUMENT_INVALIDDATETIMEKIND), "kind"); 
            }
            long long ticks = calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks(); 
            ticks += millisecond * TicksPerMillisecond;
            if (ticks < MinTicks || ticks > MaxTicks) 
            {
                throw ArgumentException(Environment::GetResourceString(Resources::ARG_DATETIMERANGE));
            }
            data = ((unsigned long long)ticks | ((unsigned long long)kind << KindShift));
        }
 
        HWIN_EXPORT DateTime DateTime::Add(const TimeSpan& value) const 
        {
            return AddTicks(value.ticks); 
        }

        HWIN_EXPORT DateTime DateTime::Add(double value, int scale) const
        {
            long long millis = (long long)(value * scale + (value >= 0? 0.5: -0.5)); 
            if (millis <= -MaxMillis || millis >= MaxMillis)
            {
                throw ArgumentOutOfRangeException("value", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_ADDVALUE));
            }
            return AddTicks(millis * TicksPerMillisecond); 
        }

        HWIN_EXPORT DateTime DateTime::AddDays(double value) const
        { 
            return Add(value, MillisPerDay);
        }

        HWIN_EXPORT DateTime DateTime::AddHours(double value) const
        {
            return Add(value, MillisPerHour);
        }
 
        HWIN_EXPORT DateTime DateTime::AddMilliseconds(double value) const
        {
            return Add(value, 1);
        } 

        HWIN_EXPORT DateTime DateTime::AddMinutes(double value) const
        {
            return Add(value, MillisPerMinute); 
        }
 
        HWIN_EXPORT DateTime DateTime::AddMonths(int months) const
        { 
            if (months < -120000 || months > 120000) 
            {
                throw ArgumentOutOfRangeException("months", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_DATETIMEBADMONTHS)); 
            }
            int y = GetDatePart(DatePart::Year); 
            int m = GetDatePart(DatePart::Month);
            int d = GetDatePart(DatePart::Day);
            int i = m - 1 + months;
            if (i >= 0) 
            { 
                m = i % 12 + 1;
                y = y + i / 12; 
            } 
            else 
            {
                m = 12 + (i + 1) % 12; 
                y = y + (i - 11) / 12;
            }
            if (y < 1 || y > 9999) 
            {
                throw ArgumentOutOfRangeException("months", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_DATEARITHMETIC)); 
            }
            int days = DaysInMonth(y, m); 
            if (d > days) 
            {
                d = days; 
            }
            return DateTime((unsigned long long)(DateToTicks(y, m, d) + (data & TicksMask) % TicksPerDay) | (data & FlagsMask));
        } 

        HWIN_EXPORT DateTime DateTime::AddSeconds(double value) const
        {
            return Add(value, MillisPerSecond); 
        }

        HWIN_EXPORT DateTime DateTime::AddTicks(long long value) const
        { 
            long long ticks = data & TicksMask;
            if (value > MaxTicks - ticks || value < MinTicks - ticks) 
            { 
                throw ArgumentOutOfRangeException("value", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_DATEARITHMETIC));
            }
            return DateTime((unsigned long long)(ticks + value) | (data & FlagsMask));
        } 

        HWIN_EXPORT DateTime DateTime::AddYears(int value) const
        {
            if (value < -10000 || value > 10000) 
            {
                throw ArgumentOutOfRangeException("years", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_DATETIMEBADYEARS)); 
            }
            return AddMonths(value * 12);
        } 

        HWIN_EXPORT const DateTime& DateTime::AssingTo(SYSTEMTIME& systemTime) const
        {
            long long filetime = data & TicksMask;
            filetime -= FileTimeOffset;
            FileTimeToSystemTime((FILETIME*)&filetime,&systemTime);
            return *this;
        }

        HWIN_EXPORT std::string DateTime::DateToAnsiString() const
        {
            char buffer[128];
            SYSTEMTIME systemTime;
            AssingTo(systemTime);
            size_t length = GetDateFormatA(LOCALE_USER_DEFAULT, 0,&systemTime,nullptr,buffer,128);
            if(length == 0)
            {
                ThrowLastOSError();
            }
            std::string result(buffer,length);
            return result;
        }
        HWIN_EXPORT String DateTime::DateToString() const
        {
            wchar_t buffer[128];
            SYSTEMTIME systemTime;
            AssingTo(systemTime);
            size_t length = GetDateFormatW(LOCALE_USER_DEFAULT, 0,&systemTime,nullptr,buffer,128);
            if(length == 0)
            {
                ThrowLastOSError();
            }
            String result(buffer,length);
            return result;
        }

        HWIN_EXPORT std::string DateTime::TimeToAnsiString() const
        {
            char buffer[128];
            SYSTEMTIME systemTime;
            AssingTo(systemTime);
            size_t length = GetTimeFormatA(LOCALE_USER_DEFAULT, 0,&systemTime,nullptr,buffer,128);
            if(length == 0)
            {
                ThrowLastOSError();
            }
            std::string result(buffer,length);
            return result;
        }
        HWIN_EXPORT String DateTime::TimeToString() const
        {
            wchar_t buffer[128];
            SYSTEMTIME systemTime;
            AssingTo(systemTime);
            size_t length = GetTimeFormatW(LOCALE_USER_DEFAULT, 0,&systemTime,nullptr,buffer,128);
            if(length == 0)
            {
                ThrowLastOSError();
            }
            String result(buffer,length);
            return result;
        }

        HWIN_EXPORT std::string DateTime::ToAnsiString() const
        {
            char buffer[128];
            SYSTEMTIME systemTime;
            AssingTo(systemTime);
            int length = GetDateFormatA(LOCALE_USER_DEFAULT, 0,&systemTime,nullptr,buffer,128);
            if(length == 0)
            {
                ThrowLastOSError();
            }
            buffer[length] = '\x20';

            int length2 = GetTimeFormatA(LOCALE_USER_DEFAULT, 0,&systemTime,nullptr,&buffer[length+1],128-(length+1));
            if(length2 == 0)
            {
                ThrowLastOSError();
            }

            std::string result(buffer,size_t(length+length2+1));
            return result;
        }

        HWIN_EXPORT String DateTime::ToString() const
        {
            wchar_t buffer[128];
            SYSTEMTIME systemTime;
            AssingTo(systemTime);
            int length = GetDateFormatW(LOCALE_USER_DEFAULT, 0,&systemTime,nullptr,buffer,128);
            if(length == 0)
            {
                ThrowLastOSError();
            }
            buffer[length] = '\x20';

            int length2 = GetTimeFormatW(LOCALE_USER_DEFAULT, 0,&systemTime,nullptr,&buffer[length+1],128-(length+1));
            if(length2 == 0)
            {
                ThrowLastOSError();
            }

            String result(buffer,size_t(length+length2+1));
            return result;
        }


        HWIN_EXPORT int DateTime::Compare(const DateTime& t1, const DateTime& t2) 
        {
            long long ticks1 = t1.data & TicksMask; 
            long long ticks2 = t2.data & TicksMask; 
            if (ticks1 > ticks2) return 1;
            if (ticks1 < ticks2) return -1; 
            return 0;
        }

 
        HWIN_EXPORT int DateTime::CompareTo(const DateTime& value) const
        {
            long long valueTicks = value.data & TicksMask; 
            long long ticks = data & TicksMask; 
            if (ticks > valueTicks) 
            {
                return 1;
            }
            if (ticks < valueTicks) 
            {
                return -1; 
            }
            return 0;
        }

            
        HWIN_EXPORT int DateTime::DaysInMonth(int year, int month) 
        { 
            if (month < 1 || month > 12) 
            {
                throw ArgumentOutOfRangeException("month", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_MONTH));
            }
            const int* days = IsLeapYear(year)? DaysToMonth366: DaysToMonth365;
            return days[month] - days[month - 1];
        } 

        HWIN_EXPORT long long DateTime::DoubleDateToTicks(double value) 
        {
            if (value >= OADateMaxAsDouble || value <= OADateMinAsDouble) 
            {
                throw new ArgumentException(Environment::GetResourceString(Resources::ARG_OLEAUTDATEINVALID));
            }
            long long millis = (long long)(value * MillisPerDay + (value >= 0? 0.5: -0.5));

            // fixup for negative milliseconds
            if (millis < 0) 
            { 
                millis -= (millis % MillisPerDay) * 2; 
            }
 
            millis += DoubleDateOffset / TicksPerMillisecond;

            if (millis < 0 || millis >= MaxMillis) 
            {
                throw new ArgumentException(Environment::GetResourceString(Resources::ARG_OLEAUTDATESCALE));
            }
            return millis * TicksPerMillisecond; 
        }
 


        HWIN_EXPORT DateTime DateTime::FromBinary(long long data)
        {
            long long ticks = data & TicksMask; 
            if (ticks < MinTicks || ticks > MaxTicks) 
            {
                throw ArgumentException(Environment::GetResourceString(Resources::ARGUMENT_DATETIMEBADBINARYDATA), "data");
            }
            return DateTime(data); 
        }

        HWIN_EXPORT DateTime DateTime::FromFileTime(long long fileTime) 
        { 
            return FromFileTimeUtc(fileTime).ToLocalTime();
        } 

        HWIN_EXPORT DateTime DateTime::FromFileTimeUtc(long long fileTime) 
        {
            if (fileTime < 0 || fileTime > MaxTicks - FileTimeOffset) 
            {
                throw new ArgumentOutOfRangeException("fileTime", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_FILETIMEINVALID)); 
            }
            
            long long universalTicks = fileTime + FileTimeOffset; 
            return DateTime(universalTicks, DateTimeKind::Utc);
        }

        HWIN_EXPORT DateTime DateTime::FromOADate(double d) 
        { 
            return DateTime(DoubleDateToTicks(d), DateTimeKind::Unspecified); 
        }
 

        HWIN_EXPORT bool DateTime::IsDaylightSavingTime() const
        { 
            if ( Kind() == DateTimeKind::Utc) 
            { 
                return false;
            }

            USHORT year = USHORT(Year());
            TIME_ZONE_INFORMATION tzi;
            if(GetTimeZoneInformationForYear(year,nullptr,&tzi) == FALSE)
            {
                ThrowLastOSError();
            }

            tzi.DaylightDate.wYear = year;
            tzi.StandardDate.wYear = year;

            long long daylightDateInTicks = SystemTimeToTicks( tzi.DaylightDate );
            long long standardDateInTicks = SystemTimeToTicks( tzi.StandardDate );
            long long ticks = data & TicksMask;


            if(daylightDateInTicks > standardDateInTicks)
            {
                if((ticks > standardDateInTicks)&&( ticks < daylightDateInTicks) )
                {
                    return true;
                }
            }
            else if(daylightDateInTicks < standardDateInTicks)
            {
                if((ticks > daylightDateInTicks)&&( ticks < standardDateInTicks) )
                {
                    return true;
                }
            }
            return false;
        }

        HWIN_EXPORT DateTime DateTime::SpecifyKind(const DateTime& value, DateTimeKind kind) 
        { 
            return DateTime(value.data & TicksMask, kind);
        } 

        HWIN_EXPORT long long DateTime::ToBinary() const
        {
            return data;
        } 

        HWIN_EXPORT DateTime DateTime::Date() const 
        { 
            long long ticks = data & TicksMask;
            return DateTime((unsigned long long)(ticks - ticks % TicksPerDay) | (data & FlagsMask)); 
        } 
 
        HWIN_EXPORT int DateTime::GetDatePart(DatePart part) const
        {
            long long ticks = data & TicksMask;
            int n = (int)(ticks / TicksPerDay); 
            int periodsOf400years = n / DaysPer400Years; 
            n -= periodsOf400years * DaysPer400Years;
            int periodsOf100years = n / DaysPer100Years;
            if (periodsOf100years == 4) 
            {
                periodsOf100years = 3;
            }
            n -= periodsOf100years * DaysPer100Years;
            int periodsOf4years = n / DaysPer4Years; 
            n -= periodsOf4years * DaysPer4Years; 
            int y1 = n / DaysPerYear;
            if (y1 == 4)
            {
                y1 = 3; 
            }

            if (part == DatePart::Year) 
            { 
                return periodsOf400years * 400 + periodsOf100years * 100 + periodsOf4years * 4 + y1 + 1; 
            }
            n -= y1 * DaysPerYear;
            if (part == DatePart::DayOfYear) 
            {
                return n + 1;
            }

            bool leapYear = y1 == 3 && (periodsOf4years != 24 || periodsOf100years == 3);

            const int* days = leapYear? DaysToMonth366: DaysToMonth365; 

            int m = (n >> 5) + 1;

            while (n >= days[m]) 
            {
                m++;
            }
            if (part == DatePart::Month) 
            {
                return m;
            }
            return n - days[m - 1] + 1; 
        }
 
        HWIN_EXPORT int DateTime::Day() const 
        { 
            return GetDatePart(DatePart::Day);
        }

        HWIN_EXPORT DayOfWeek DateTime::DayOfWeek() const 
        {
            long long ticks = data & TicksMask;
            return (harlinn::windows::DayOfWeek)((ticks / TicksPerDay + 1) % 7); 
        }
 
        HWIN_EXPORT int DateTime::DayOfYear() const
        {
            return GetDatePart(DatePart::DayOfYear);
        }


        HWIN_EXPORT int DateTime::Hour( ) const
        { 
            long long ticks = data & TicksMask;
            return (int)((ticks / TicksPerHour) % 24);
        } 


        HWIN_EXPORT DateTimeKind DateTime::Kind() const 
        {
            long long kind = data & FlagsMask;
            switch (kind) 
            {
                case KindUnspecified:
                    return DateTimeKind::Unspecified;
                case KindUtc: 
                    return DateTimeKind::Utc;
                default: 
                    return DateTimeKind::Local; 
            }
        }

        HWIN_EXPORT int DateTime::Millisecond() const 
        { 
            long long ticks = data & TicksMask;
            return (int)((ticks/ TicksPerMillisecond) % 1000); 
        } 
 
        HWIN_EXPORT int DateTime::Minute() const 
        {
            long long ticks = data & TicksMask;
            return (int)((ticks / TicksPerMinute) % 60); 
        }

        HWIN_EXPORT int DateTime::Month() const 
        {
            return GetDatePart(DatePart::Month);
        }

 
            
        HWIN_EXPORT DateTime DateTime::Now() 
        {
            DateTime utc = UtcNow(); 
            return utc.ToLocalTime();
        } 

        HWIN_EXPORT DateTime DateTime::UtcNow() 
        {
            long long ticks = 0;
            GetSystemTimeAsFileTime((FILETIME*)&ticks);
            return DateTime( ((unsigned long long)(ticks + FileTimeOffset)) | KindUtc);
        }

        HWIN_EXPORT int DateTime::Second() const
        {
            long long ticks = data & TicksMask;
            return (int)((ticks / TicksPerSecond) % 60); 
        }
 
        HWIN_EXPORT long long DateTime::Ticks() const
        { 
            long long ticks = data & TicksMask;
            return ticks; 
        }
         

        HWIN_EXPORT TimeSpan DateTime::TimeOfDay() const 
        {
            long long ticks = data & TicksMask;
            return TimeSpan(ticks % TicksPerDay);
        }
 
        HWIN_EXPORT DateTime DateTime::Today() 
        { 
            return DateTime::Now().Date();
        } 

        HWIN_EXPORT int DateTime::Year() const
        { 
            return GetDatePart(DatePart::Year); 
        } 
        
        HWIN_EXPORT bool DateTime::IsLeapYear(int year) 
        {
            if (year < 1 || year > 9999) 
            {
                throw ArgumentOutOfRangeException("year", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_YEAR));
            } 
            return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); 
        } 

        HWIN_EXPORT TimeSpan DateTime::Subtract(const DateTime& value) const
        { 
            long long ticks = data & TicksMask;
            long long valueTicks = value.data & TicksMask;
            return TimeSpan(ticks - valueTicks);
        } 
 
        HWIN_EXPORT DateTime DateTime::Subtract(const TimeSpan& value) const
        {
            long long ticks = data & TicksMask; 
            long long valueTicks = value.ticks;
            if (ticks - MinTicks < valueTicks || ticks - MaxTicks > valueTicks) 
            {
                throw ArgumentOutOfRangeException("value", Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_DATEARITHMETIC));
            } 
            return DateTime((unsigned long long)(ticks - valueTicks) | (data & FlagsMask));
        } 
 
        HWIN_EXPORT double DateTime::TicksToOADate(long long value) 
        { 
            if (value == 0)
            {
                return 0.0;
            }
            if (value < TicksPerDay)
            {
                value += DoubleDateOffset;
            }
            if (value < OADateMinAsTicks)
            {
                throw OverflowException(Environment::GetResourceString(Resources::ARG_OLEAUTDATEINVALID)); 
            }
            long long millis = (value  - DoubleDateOffset) / TicksPerMillisecond; 
            if (millis < 0) 
            {
                long long frac = millis % MillisPerDay;
                if (frac != 0) 
                {
                    millis -= (MillisPerDay + frac) * 2;
                }
            } 
            return ((double)millis) / MillisPerDay;
        } 
 
        HWIN_EXPORT double DateTime::ToOADate() const
        {
            long long ticks = data & TicksMask; 
            return TicksToOADate(ticks);
        }
 
        HWIN_EXPORT long long DateTime::ToFileTime() const
        {
            return ToUniversalTime().ToFileTimeUtc(); 
        }
 
        HWIN_EXPORT long long DateTime::ToFileTimeUtc() const
        {
            long long ticks = data & KindLocal ? ToUniversalTime().data & TicksMask : data & TicksMask;
            ticks -= FileTimeOffset; 
            if (ticks < 0) 
            {
                throw ArgumentOutOfRangeException(nullptr, Environment::GetResourceString(Resources::ARGUMENTOUTOFRANGE_FILETIMEINVALID)); 
            } 
            return ticks;
        } 

        HWIN_EXPORT DateTime DateTime::ToLocalTime() const
        {
            if (data & KindLocal) 
            {
                return *this; 
            }
            long long ticks = data & TicksMask;
            ticks -= FileTimeOffset; 
            long long localTicks = 0;
            FileTimeToLocalFileTime((FILETIME*)&ticks,(FILETIME*)&localTicks);
            localTicks += FileTimeOffset;
            return DateTime(localTicks, DateTimeKind::Local);
        }
 
        HWIN_EXPORT DateTime DateTime::ToUniversalTime() const
        { 
            if (data & KindUtc) 
            {
                return *this; 
            }
            long long ticks = data & TicksMask;
            ticks -= FileTimeOffset; 
            long long utcTicks = 0;
            LocalFileTimeToFileTime((FILETIME*)&ticks,(FILETIME*)&utcTicks);
            utcTicks += FileTimeOffset;
            return DateTime(utcTicks, DateTimeKind::Utc);
        } 

        HWIN_EXPORT bool DateTime::TryCreate(int year, int month, int day, int hour, int minute, int second, int millisecond, DateTime& result) 
        {
            result = MinValue; 
            if (year < 1 || year > 9999 || month < 1 || month > 12) 
            { 
                return false;
            } 
            const int* days = IsLeapYear(year) ? DaysToMonth366 : DaysToMonth365;
            if (day < 1 || day > days[month] - days[month - 1]) 
            {
                return false;
            } 
            
            if (hour < 0 || hour >= 24 || minute < 0 || minute >= 60 || second < 0 || second >= 60) 
            {
                return false; 
            } 
            if (millisecond < 0 || millisecond >= MillisPerSecond) 
            {
                return false; 
            }
            long long ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second);

            ticks += millisecond * TicksPerMillisecond; 
            if (ticks < MinTicks || ticks > MaxTicks) 
            {
                return false; 
            } 
            result = DateTime(ticks, DateTimeKind::Unspecified);
            return true; 
        }

        // ----------------------------------------------------------------------
        // Stopwatch
        // ----------------------------------------------------------------------
        long long Stopwatch::frequency( TicksPerSecond ); 
        HWIN_EXPORT const bool Stopwatch::IsHighResolution = Stopwatch::InitializeStopwatch();
        double Stopwatch::tickFrequency = 1.0;

        bool Stopwatch::InitializeStopwatch() 
        {
            long long frq = 0;
            bool result = QueryPerformanceFrequency((LARGE_INTEGER*)&frq) != FALSE; 
            if(result) 
            {
                frequency = frq;
                tickFrequency = double(TicksPerSecond)/double(frequency);
            }
            return result;
        }

        HWIN_EXPORT long long Stopwatch::Frequency()
        {
            return frequency;
        }


        HWIN_EXPORT Stopwatch::Stopwatch() 
            : elapsedTicks(0),
              isRunning(false),
              startedAt(0)
        { 
            
        } 
 
        HWIN_EXPORT void Stopwatch::Start() 
        {
            if(!isRunning) 
            {
                startedAt = GetTimestamp();
                isRunning = true;
            } 
        }

        HWIN_EXPORT void Stopwatch::Restart() 
        { 
            elapsedTicks = 0;
            startedAt = GetTimestamp(); 
            isRunning = true; 
        }


        HWIN_EXPORT Stopwatch Stopwatch::StartNew() 
        { 
            Stopwatch result;
            result.Start(); 
            return result;
        }

        HWIN_EXPORT void Stopwatch::Stop() 
        { 
            if( isRunning) 
            { 
                isRunning = false;

                long long endTimeStamp = GetTimestamp(); 
                long long elapsedThisPeriod = endTimeStamp - startedAt;
                elapsedTicks += elapsedThisPeriod; 

                if(elapsedTicks < 0)
                {
                    elapsedTicks = 0;
                }
            }
        } 

        HWIN_EXPORT void Stopwatch::Reset() 
        { 
            elapsedTicks = 0; 
            isRunning = false;
            startedAt = 0; 
        }

 
        HWIN_EXPORT bool Stopwatch::IsRunning() const 
        {
            return isRunning;
        }
 
        HWIN_EXPORT TimeSpan Stopwatch::Elapsed() const
        {
            return TimeSpan( GetElapsedDateTimeTicks()); 
        } 

        HWIN_EXPORT long long Stopwatch::ElapsedMilliseconds() const
        { 
            return GetElapsedDateTimeTicks()/TicksPerMillisecond; 
        }

        HWIN_EXPORT long long Stopwatch::ElapsedTicks() const
        { 
            long long timeElapsed = elapsedTicks;
 
            if( isRunning) 
            {
                long long currentTimeStamp = GetTimestamp();
                long long elapsedUntilNow = currentTimeStamp - startedAt; 
                timeElapsed += elapsedUntilNow;
            }
            return timeElapsed; 
        } 
 
        HWIN_EXPORT long long Stopwatch::GetTimestamp() 
        {
            if(IsHighResolution) 
            { 
                long long timestamp = 0;
                QueryPerformanceCounter((LARGE_INTEGER*)&timestamp);
                return timestamp;
            } 
            else 
            {
                return DateTime::UtcNow().Ticks(); 
            } 
        }

        HWIN_EXPORT double Stopwatch::GetTimestampInSeconds()
        {
            double ts = tickFrequency * double(Stopwatch::GetTimestamp())*SecondsPerTick; 
            return ts;
        }
        HWIN_EXPORT double Stopwatch::GetTimestampInMilliseconds()
        {
            double ts = tickFrequency * double(Stopwatch::GetTimestamp())*MillisecondsPerTick;
            return ts;
        }

        long long Stopwatch::GetElapsedDateTimeTicks() const
        { 
            long long ticks = ElapsedTicks();
            if( IsHighResolution) 
            { 
                double dticks = double(ticks);
                dticks *= tickFrequency;
                return long long(dticks); 
            }
            else 
            { 
                return ticks; 
            }
        } 

    }
}